#include "VStdAfx.h"
#include "VThreading.h"

#if VPLATFORM_WIN32
VSyncEvent::VSyncEvent(void):m_hevent(NULL)
{

}

VSyncEvent::~VSyncEvent(void)
{
	if (m_hevent != NULL)
	{
		CloseHandle(m_hevent);
	}
}

/**
* Waits for the event to be signaled before returning
*/
void VSyncEvent::Lock(void)
{
	WaitForSingleObject(m_hevent,INFINITE);
}

/**
* Triggers the event so any waiting threads are allowed access
*/
void VSyncEvent::Unlock(void)
{
	PulseEvent(m_hevent);
}

BOOL VSyncEvent::Create(BOOL ManualReset,const char* InName)
{
	m_hevent = CreateEventA(NULL,ManualReset,0,InName);
	return m_hevent != NULL;
}

void VSyncEvent::Trigger(void)
{
	VASSERT(m_hevent);
	SetEvent(m_hevent);
}

void VSyncEvent::Reset(void)
{
	VASSERT(m_hevent);
	ResetEvent(m_hevent);
}

// 
BOOL VSyncEvent::Wait(UINT WaitTime)
{
	VASSERT(m_hevent);
	return WaitForSingleObject(m_hevent,WaitTime) == WAIT_OBJECT_0;
}

//////////////////////////////////////////////////////////////////////////
// Thread
//////////////////////////////////////////////////////////////////////////

/**
 * Code setting the thread name for use in the debugger.
 *
 * http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx
 */
#define MS_VC_EXCEPTION 0x406D1388

#pragma pack(push,8)
typedef struct tagTHREADNAME_INFO
{
	DWORD dwType;		// Must be 0x1000.
	LPCSTR szName;		// Pointer to name (in user addr space).
	DWORD dwThreadID;	// Thread ID (-1=caller thread).
	DWORD dwFlags;		// Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)


void SetThreadName( DWORD ThreadID, LPCSTR ThreadName )
{
	Sleep(10);
	THREADNAME_INFO ThreadNameInfo;
	ThreadNameInfo.dwType		= 0x1000;
	ThreadNameInfo.szName		= ThreadName;
	ThreadNameInfo.dwThreadID	= ThreadID;
	ThreadNameInfo.dwFlags		= 0;
	__try
	{
		RaiseException( MS_VC_EXCEPTION, 0, sizeof(ThreadNameInfo)/sizeof(ULONG_PTR), (ULONG_PTR*)&ThreadNameInfo );
	}
	__except(EXCEPTION_EXECUTE_HANDLER)
	{
	}
}


#elif VPLATFORM_ANDROID

typedef enum
{
	TRIGGERED_NONE,
	TRIGGERED_ONE,
	TRIGGERED_ALL,
	TRIGGERED_PULSE,
} TriggerType;

struct pthread_event 
{
	pthread_event()
	{
		bInitialized = FALSE;
		bIsManualReset = FALSE;
		Triggered = TRIGGERED_NONE;
		WaitingThreads = 0;
	}
	~pthread_event()
	{
		if (bInitialized)
		{
			// try to flush out any waiting threads...
			LockEventMutex();
			bIsManualReset = TRUE;
			UnlockEventMutex();
			Trigger();  // any waiting threads should start to wake up now.

			LockEventMutex();
			bInitialized = FALSE;  // further incoming calls to this object will now crash in check().
			while (WaitingThreads)  // spin while waiting threads wake up.
			{
				UnlockEventMutex();  // cycle through waiting threads...
				LockEventMutex();
			}
			// No threads are currently waiting on Condition and we hold the Mutex. Kill it.
			pthread_cond_destroy(&Condition);
			// Unlock and kill the mutex, since nothing else can grab it now.
			UnlockEventMutex();
			pthread_mutex_destroy(&Mutex);
		}
	}
	void LockEventMutex()
	{
		const int rc = pthread_mutex_lock(&Mutex);
		VASSERT(rc == 0);
	}

	void UnlockEventMutex()
	{
		const int rc = pthread_mutex_unlock(&Mutex);
		VASSERT(rc == 0);
	}
	BOOL bInitialized;
	BOOL bIsManualReset;
	volatile TriggerType Triggered;
	volatile INT WaitingThreads;
	pthread_mutex_t Mutex;
	pthread_cond_t Condition;
};

VSyncEvent::VSyncEvent(void)
{
	m_event = new pthread_event;
}

VSyncEvent::~VSyncEvent(void)
{
	delete m_event;
}

void VSyncEvent::Lock(void)
{
	Wait((UINT)-1);  // infinite wait.
}

void VSyncEvent::Unlock(void)
{
	PulseEvent(m_hevent);
}

BOOL VSyncEvent::Create(BOOL ManualReset,const char* InName)
{
	VASSERT(m_event && !m_event->bInitialized)
	BOOL RetVal = FALSE;
	m_event->Triggered = TRIGGERED_NONE;
	m_event->bIsManualReset = ManualReset;

	if (pthread_mutex_init(&m_event->Mutex, NULL) == 0)
	{
		if (pthread_cond_init(&m_event->Condition, NULL) == 0)
		{
			m_event->bInitialized = TRUE;
			RetVal = TRUE;
		}
		else
		{
			pthread_mutex_destroy(&m_event->Mutex);
		}
	}
	return RetVal;
}

void VSyncEvent::Trigger(void)
{
	VASSERT(m_hevent);
	SetEvent(m_hevent);
}

void VSyncEvent::Reset(void)
{
	VASSERT(m_hevent);
	ResetEvent(m_hevent);
}

// 
BOOL VSyncEvent::Wait(UINT WaitTime)
{
	VASSERT(m_hevent);
	return WaitForSingleObject(m_hevent,WaitTime) == WAIT_OBJECT_0;
}
#elif VPLATFORM_IOS

#else 

#endif 

//////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////

#if VPLATFORM_WIN32
DWORD WINAPI VThread::_ThreadProc(LPVOID data) 
{
	VThread* thread = (VThread*)data;
	thread->Process();
	return 0;
}
#elif VPLATFORM_WINRT
int VThread::_ThreadProc(void *data) {
	VThread* thread = (VThread*)data;
	thread->Process();
	return 0;
}
#else
void* VThread::_ThreadProc(void *data)
{
	VThread* thread = (VThread*)data;
#if defined(_LINUX) || defined(_MACOS) || defined(_IOS)
	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
#endif
	thread->Process();
	pthread_exit(NULL);
	return NULL;
}
#endif

VThread::VThread():
#if VPLATFORM_WIN32
m_hThread(NULL),m_hEvent(NULL),m_ThreadId(0)
#else 
#endif 
{

}

VThread::~VThread()
{

}

BOOL VThread::Run(UINT StackSize)
{
#if VPLATFORM_WIN32
	VASSERT(m_hThread == NULL);
	DWORD ThreadId;
	m_hThread = CreateThread(NULL,(SIZE_T)StackSize,&VThread::_ThreadProc,this,0,&ThreadId);
	m_ThreadId = (UINT)ThreadId;
return (m_hThread != NULL);
#elif VPLATFORM_WINRT
	return (thrd_create(&m_hThread,&VThread::_ThreadProc,this) == thrd_success);
#else
	pthread_attr_t attr;
	pthread_attr_init(&attr);
	pthread_attr_setstacksize(&attr,StackSize);
	pthread_create(&m_hThread,&attr,&VThread::_ThreadProc,this);
	return (pthread_detach(m_hThread) == 0);
#endif

}

void VThread::SetThreadPriority(EVThreadPriority NewPriority)
{

}
void VThread::SetProcessorAffinity(DWORD ProcessorNum)
{

}

void VThread::Suspend()
{
	VASSERT(m_hThread);
	SuspendThread(m_hThread);
}

void VThread::Resume()
{
	VASSERT(m_hThread);
	ResumeThread(m_hThread);
}

BOOL VThread::Terminate(BOOL bShouldWait)
{
	return TRUE;
}

void VThread::WaitForCompletion(void)
{
	WaitForSingleObject(m_hThread,INFINITE);
}

UINT VThread::GetThreadID(void)
{
	return 0;
}

void VThread::SetName(const char* ThreadName)
{
#if VPLATFORM_WIN32

	THREADNAME_INFO threadName;
	threadName.dwType = 0x1000;
	threadName.szName = ThreadName;
	threadName.dwThreadID = GetCurrentThreadId();
	threadName.dwFlags = 0;

	__try
	{
		RaiseException( 0x406D1388, 0, sizeof(threadName)/sizeof(DWORD), (ULONG_PTR*)&threadName );
	}
	__except (EXCEPTION_CONTINUE_EXECUTION)
	{
	}
#endif 
}